在 ES6 新增兩種變數方法 let
、 const
,不過我們比較常把 const
叫做常數,主要是因為 var
的特性 容易觸發 Bug , 這邊與 var
的差異主要有:
在提及這些不同特性之前,先大致介紹一下這兩個用法的差別。
let
能夠重新賦值,但不能重複宣告
let String1 = 'test1'
String1 = 'test2'
let String1 = 'test3' // 重複宣告錯誤, Uncaught SyntaxError: Identifier 'String1' has already been declared
const
不能重新賦值,也不能重新宣告。
const String1 = 'test1'
const String1 = 'test2' // 重複宣告錯誤, Uncaught SyntaxError: Identifier 'String1' has already been declared
String1 = 'test3' // 賦值錯誤, Uncaught TypeError: invalid assignment to const 'String1'
要補充一點如果 const
的值是物件,對物件底下的屬性賦值, const
則能接受這種操作繼續使用。
const obj = {}
obj.name = 'Ryder'
console.log(obj) // { name: 'Ryder' }
const array = []
array.push(1)
console.log(array) // [1]
在過去 var
作用域是根據函式作用域,而 let
、 const
則是以 { ... }
Block 區塊做為作用域,來看看以下範例:
var name1 = 'Ryder'
let name2 = 'Ryder'
function test(){
var name1 = 'Jack'
let name2 = 'Jack'
}
test()
console.log(name1, name2) // ?
結果是 Ryder, Ryder
,這範例很好理解,不論是 var
還是 let
, name = 'Jack'
的作用範圍都只在 test()
這個函式中,console 的位置則是在全域,自然都會是 Ryder
,那麼再來看看這個範例:
var name1 = 'Ryder'
let name2 = 'Ryder'
{
var name1 = 'Jack'
}
{
let name2 = 'Jack'
}
console.log(name1 , name2) // ?
結果會是 Jack, Ryder
,這是因為上面提到的, let
是以 { ... }
區塊做為作用域,因此 let name1 = 'Jack'
這個語法的有效範圍只會存在於 { ... }
之中,而 var
則會被 { ... }
中的 var name1 = 'Jack'
,直接做替換,因此 name1
會是 'Jack'
。
這邊要補充一下, 這邊提到的 { ... }
並不是物件,而是一個作用域範圍,主要是為了搭配 let
、 const
特性 ES6 才引入的,不過實做中通常不會刻意使用 { ... }
去區分作用域。
在提升章節我們有提到,JavaScript 在編譯程式碼時,會分為兩個階段:
1.創造階段
2.執行階段var
變數會先在 創造階段 被建立,進入執行階段才會實際賦值,而在創造階段中的 var
變數,他的值會是 undefined
,如這個範例:
console.log(name1) // undefined
var name1 = 'Ryder'
let
雖然也有提升概念,也同樣分成:
1.創造階段
2.執行階段
但在創造階段和 var
不同,let
在創造階段不是直接顯示 undefined
,他是進入一個 暫時性死區 (TDZ) 的狀況,MDN 文件是這麼描述的:
The variable is in a "temporal dead zone" from the start of the block until the initialization is processed
如果我們在 暫時性死區 的狀態去取得 let
變數的值,瀏覽器會跳錯,要注意的是,不同瀏覽器跳出的錯誤訊息會不同,如下範例:
FireFox 版本
console.log(name2) // Uncaught ReferenceError: can't access lexical declaration 'name1' before initialization
let name2 = 'Ryder'
Chrome 版本
console.log(name2) //Uncaught ReferenceError: name1 is not defined
let name2 = 'Ryder'
上面有提到到 let
、 const
是根據 { ... }
來區分作用域的,這邊要提一點的是 var
全域建立時,會是掛在 window 下。
而使用 let
、 const
在全域建立變數時,他並不會掛在 window 下,但我們若直接呼叫變數,他也會正確顯示,如範例
var name1= 'Ryder'
let name2 = 'Ryder'
window.name1 // 'Ryder'
window.name2 // undefined
name1 // 'Ryder'
name2 // 'Ryder'
這個原因是出在全域執行環境(Global space) 上面,首先這個全域執行環境其實是由兩個環境所組成的
因此全域執行環境(Global space) 其實是一個由雙環境組成的東西,一般來說我們是看不到 Declare Env 的。
所以 var
其實是基於 ObjectEnv 宣告並加入到 Declare Env,而 let
、const
則是只會宣告在 Declare Env 中,這也就是為什麼我們無法在 Window 上面看到由 let
、const
宣告的變數但卻又可以正常取得到值的原因。